package com.wakyde.collection;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * HashMap的底层实现？
 * HashMap的长度为什么是2的幂次方
 * ConcurrentHashMap和Hashtable的区别？
 * ConcurrentHashMap线程安全的具体实现方式/底层具体实现
 */
public class MapDemo02 {
    public static void main(String[] args) {
        // HashMap的底层实现？
        /*
            1.创建HashMap对象
            2.HashMap的put方法:
                调用putVal()之前，先获取key的hash值
                putVal() =》
                然后进行初始扩容为16
                判断数组(n-1)&hash的位置下的值是否为null, 如果为空, 就往数组里面添加新的节点
                否则
                    如果hash相等且key相等，就会覆盖vaule值
                    否则如果节点的类型是TreeNode,就调用putTreeVal()
                    否则循环链表，将数据插入到链表中，插入之后判断，如果链表长度大于或等于了8，则调用treeifyBin()来判断是否需要转换为红黑树
                    treeifyBin() =》
                    如果数组的长度小于64，则对数组进行扩容，否则，将链表转换为红黑树treeify()
                然后判断是否扩容的条件是: hashMap的大小加1是否 大于 当前阈值(当前容量*加载因子（0.75）)；如果大于就扩容，扩容的大小是2的幂次方
         */

        Map<String, Object> map = new HashMap<>();

        Map<Integer, List<String>> param = new HashMap<>();
        for (int i = 33; i < 1000; i++) {
            char ch = (char) i;
            String str = String.valueOf(ch);
            int index = 15 & str.hashCode();
            List<String> list = param.get(index);
            if (list == null) {
                list = new ArrayList<>();
            }
            list.add(str);
            param.put(index,list);
        }
        param.forEach((k,v) -> {
            System.out.println(k + " " + Arrays.toString(v.toArray()));
            if(k.equals(1)){
                for (String s : v) {
                    map.put(s, 1);
                }
            }
        });

        // HashMap的长度为什么是2的幂次方
        /*
            因为这样能够将数据均匀的分布在哈希表中，降低了hash碰撞
            可以通过(n-1)&hash来达到均匀分布
            当n为2幂次方时，(n-1)&hash就等价于hash%n
            为什么不直接用hash%n,是因为用按位运算符效率更高
         */

        // ConcurrentHashMap和Hashtable的区别？
        /*
            1. 实现接口区别: ConcurrentHashMap实现了ConcurrentMap， Hashtable实现了Cloneable
            2. 底层实现 ConcurrentHashMap使用了数组+链表 或者 数组+红黑树；Hashtable没有转换红黑树的机制
            3. 存放位置的计算不一样 ConcurrentHashMap: (n-1)&hash  Hashtable: (hash & 0x7FFFFFFF) % tab.length
            4. 默认的扩容和每次增加扩容的大小不一样 ConcurrentHashMap:默认16;每次按2幂次方增加容量  Hashtable: 默认11; 每次按2n增加容量
            3. 实现线程安全的方式
         */
        Map<String, String> map1 = new ConcurrentHashMap<>();
//        map1.put("a", null);
        Map<String, String> map2 = new Hashtable<>();
//        map2.put("a", null);

        // ConcurrentHashMap线程安全的具体实现方式/底层具体实现
        /*
            大量使用了CAS
            putVal() =>
            计算hash值
            循环Node数组
            如果Node数组中的值为空，initTable() => 初始化容量为16
                添加数据casTabAt()=>
            否则
                synchronized 锁住Node节点
                将数据插入到链表，如果链表的长度大于8，则调用treeifyBin()判断是否转换为红黑树
                如果Node的长度小于64则进行扩容，否则进行转换红黑树
                如果Node中的个数小于6，则将红黑树退化为链表
         */
        map1.put("a", "1");
    }
}
